using System;
using System.Linq.Expressions;
using PropertyExpression.Common;

namespace PropertyExpression.ReactiveRelation
{
    public static partial class Value
    {        
        public static IPropertyValue<TProperty> _<TEntity, TProperty>(
            this IPropertyHolder<TEntity> propertyHolder,
            Expression<Func<TEntity, TProperty>> propertyExpression)
        {
            return propertyHolder.GetPropertyValue(propertyExpression.ToPropertyExpressionProxy());
        }

        public static IValue<TResult> If<TResult>(
            this IValue<bool> boolValue,
            IValue<TResult> trueValue, IValue<TResult> falseValue)
        {
            return boolValue.OptionalResultFunc(
                _ => new
                         {
                             TrueArg = _._(trueValue),
                             FlaseArg = _._(falseValue)
                         },
                (arg1, arg2) => arg1 ? arg2.TrueArg() : arg2.FlaseArg()
                );
        }

        public static IValue<TResult> Func<T1, TResult>(
            this IValue<T1> value1,
            Func<T1, TResult> func)
        {
            return value1.Func(
                p => new { },
                (arg1, arg2) => func(arg1)
                );
        }

        public static IValue<TResult> Func<T1, T2, TResult>(
            this IValue<T1> value1, IValue<T2> value2,
            Func<T1, T2, TResult> func)
        {
            return value1.Func(
                _ => new
                         {
                             Arg2 = _._(value2)
                         },
                (arg1, arg2) => func(arg1, arg2.Arg2())
                );
        }

        public static IValue<TResult> Func<T1, T2, T3, TResult>(
            this IValue<T1> value1, IValue<T2> value2, IValue<T3> value3,
            Func<T1, T2, T3, TResult> func)
        {
            return value1.Func(
                _ => new
                         {
                             Arg2 = _._(value2),
                             Arg3 = _._(value3)
                         },
                (arg1, arg2) => func(arg1, arg2.Arg2(), arg2.Arg3())
                );
        }

        public static IValue<TResult> Func<T1, T2, T3, T4, TResult>(
            this IValue<T1> value1, IValue<T2> value2, IValue<T3> value3, IValue<T4> value4,
            Func<T1, T2, T3, T4, TResult> func)
        {
            return value1.Func(
                _ => new
                         {
                             Arg2 = _._(value2),
                             Arg3 = _._(value3),
                             Arg4 = _._(value4),
                         },
                (arg1, arg2) => func(arg1, arg2.Arg2(), arg2.Arg3(), arg2.Arg4())
                );
        }

        public static IValue<T?> ToNullable<T>(
            this IValue<T> value1) where T : struct
        {
            return value1.Func(
                arg =>
                    {
                        T? result = arg;
                        return result;
                    }
                );
        }
    }
}